//
//  WBNetworkManager.swift
//  DXWeibo
//
//  Created by dx on 2017/1/7.
//  Copyright © 2017年 dx. All rights reserved.
//

import UIKit
import AFNetworking    // 导入框架的文件夹的名字

// Swift 的枚举支持任意数据类型
// switch ／ enum 在OC中都只支持整数
/**
 - 开发中，网络请求返回的状态码是405，表示不支持的网络请求方法，首先应该查找网络请求方法是否正确
 */

enum WBHTTPMethod {
    case GET
    case POST
}

/// 网络管理工具
class WBNetworkManager: AFHTTPSessionManager {

    /// 单例 - 静态区／常量／闭包
    /// 在第一次访问时，执行闭包，并且将结果保存在 shared 常量中
    static let shared: WBNetworkManager = {
        
        // 实例化对象 
        let instance = WBNetworkManager()
        
        // 设置响应反序列化支持的数据类型
        instance.responseSerializer.acceptableContentTypes?.insert("text/plain")
        
        // 返回对象
        return instance
    }()
    
    /// 访问令牌，所有网络请求，都基于此令牌（登录除外）
    /// 为了保护用户安全，token 是有时限的，默认是三天，token 过期，返回状态码 403
    /// 懒加载用户账户属性
    lazy var userAccount = WBUserAccount()
    
    /// 用户登录标记[计算型属性]
    var userLogon: Bool {
        return userAccount.access_token != nil
    }
    
    /// 专门负责拼接 token 的网络请求方法
    ///
    /// - Parameters:
    ///   - method: GET/POST
    ///   - urlString: urlString
    ///   - parameters:  参数字典
    ///   - name: 上传文件使用的字段名
    ///   - data: 上传文件的二进制数据
    ///   - completion: 完成回调
    func tokenRequest(method: WBHTTPMethod = .GET, urlString: String, parameters: [String: Any]?, name: String? = nil, data: Data? = nil, completion: @escaping (_ json: Any?, _ isSuccess: Bool)->()) {
        
        //处理 token 字典
        // 0> 判断 token 是否为nil
        guard let token = userAccount.access_token else {
            print("没有 token！需要登录")
            // 发送通知，提示用户登录
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: WBUserShouldLoginNotification), object: nil)
            
            completion(nil, false)
            return
        }
        
        // 1> 判断参数字典是否存在，如果为 nil，应该新建一个字典
        var parameters = parameters
        if parameters == nil {
            // 实例化字典
            parameters = [String: Any]()
        }
        
        // 2> 设置参数字典
        parameters!["access_token"] = token
        
        // 3> 判断 name 和 data
        if let name = name, let data = data {
            // 上传文件
            upload(urlString: urlString, parameters: parameters, name: name, data: data, completion: completion)
        } else {
            
            // 调用 request 发起真正的网络请求
            request(method: method, urlString: urlString, parameters: parameters, completion: completion)
        }
    }
    
    // MARK: - 封装 AFN 方法
    /// 上传文件必须是 POST 方法，GET 只能获取数据
    ///
    /// - Parameters:
    ///   - urlString: urlString
    ///   - parameters:  参数字典
    ///   - name: 接收上传数据的服务器字段（name - 要咨询公司的后台）'pic'
    ///   - data: 要上传的二进制数据
    ///   - completion: 完成回调
    func upload(urlString: String, parameters: [String: Any]?, name: String, data: Data, completion: @escaping (_ json: Any?, _ isSuccess: Bool)->()) {
        
        post(urlString, parameters: parameters, constructingBodyWith: { (formData) in
            
            // 创建 formData
            // 1. data - 要上传的二进制数据
            // 2. name - 服务器接收数据的字段名
            // 3. fileName - 保存在服务器的文件名，可以乱写。图片上传完成后，会生成缩略图，中图，大图
            // 4. mimeType - 告诉服务器上传文件的类型，不想告诉的话用 application/octet-stream，否则 image/png image/jpg image/gif
            formData.appendPart(withFileData:data, name: name, fileName: "xxx", mimeType: "application/octet-stream")
            
        }, progress: nil, success: { (_, json) in
            
            completion(json, true)
            
        }) { (task, error) in
            
            if (task?.response as? HTTPURLResponse)?.statusCode == 403 {
                print("Token 过期了")
                // 发送通知，提示用户再次登陆
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: WBUserShouldLoginNotification), object: "bad token")
            }
            
            print("网络请求数据错误 \(error)")
            completion(nil, false)
        }
    }
    
    /// 封装 AFN 的 GET ／ POST 请求
    ///
    /// - Parameters:
    ///   - method: GET / POST
    ///   - urlString: URLString
    ///   - parameters: 参数字典
    ///   - completion: 完成回调 - (json(字典/数组), 是否成功)
    func request(method: WBHTTPMethod = .GET, urlString: String, parameters: [String: Any]?, completion: @escaping (_ json: Any?, _ isSuccess: Bool)->()) {    // ** @escaping 新语法！！！！！
        
        // 成功回调
        let success = { (task: URLSessionDataTask, json: Any?)->() in
            completion(json, true)
        }
        
        // 失败回调
        let failure = { (task: URLSessionDataTask?, error: Error)->() in
            
            // 针对 403 处理 token 过期
            if (task?.response as? HTTPURLResponse)?.statusCode == 403 {
                print("Token 过期了")
                // 发送通知，提示用户再次登陆
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: WBUserShouldLoginNotification), object: "bad token")
            }
           
            // error 通常比较吓人，不必传出去
            print("网络请求数据错误 \(error)")
            completion(nil, false)
        }
        
        if method == .GET {
            get(urlString, parameters: parameters, progress: nil, success: success, failure: failure)
        } else {
            post(urlString, parameters: parameters, progress: nil, success: success, failure: failure)
        }
    }
}









